
#include <system/list.h>

/**
 */

#define READ_ONCE_LIST(var) (*((volatile plist_head_t *)(&(var))))
#define WRITE_ONCE_LIST(var, val) (*((volatile plist_head_t *)(&(var))) = (val))

/**
 * These are non-NULL pointers that will result in page faults
 * under normal circumstances, used to verify that nobody uses
 * non-initialized list entries.
 */
#define LIST_POISON1  ((struct list_head *) 0x100100)
#define LIST_POISON2  ((struct list_head *) 0x200200)

/**
 */

#ifdef CONFIG_DEBUG_LIST

uint8_t __list_add_valid(struct list_head *newl, struct list_head *prev,
		      struct list_head *next)
{
	if (CHECK_DATA_CORRUPTION(next->prev != prev,
			"list_add corruption. next->prev should be prev (%px), but was %px. (next=%px).\n",
			prev, next->prev, next) ||
	    CHECK_DATA_CORRUPTION(prev->next != next,
			"list_add corruption. prev->next should be next (%px), but was %px. (prev=%px).\n",
			next, prev->next, prev) ||
	    CHECK_DATA_CORRUPTION(newl == prev || newl == next,
			"list_add double add: newl=%px, prev=%px, next=%px.\n",
			newl, prev, next))
		return 0;

	return 1;
}

uint8_t __list_del_entry_valid(struct list_head *entry)
{
	struct list_head *prev, *next;

	prev = entry->prev;
	next = entry->next;

	if (CHECK_DATA_CORRUPTION(next == LIST_POISON1,
			"list_del corruption, %px->next is LIST_POISON1 (%px)\n",
			entry, LIST_POISON1) ||
	    CHECK_DATA_CORRUPTION(prev == LIST_POISON2,
			"list_del corruption, %px->prev is LIST_POISON2 (%px)\n",
			entry, LIST_POISON2) ||
	    CHECK_DATA_CORRUPTION(prev->next != entry,
			"list_del corruption. prev->next should be %px, but was %px\n",
			entry, prev->next) ||
	    CHECK_DATA_CORRUPTION(next->prev != entry,
			"list_del corruption. next->prev should be %px, but was %px\n",
			entry, next->prev))
		return 0;

	return 1;

}

#else

static uint8_t __list_add_valid(struct list_head *newl,
	struct list_head *prev,
	struct list_head *next)
{
	return 1;
}

static uint8_t __list_del_entry_valid(struct list_head *entry)
{
	return 1;
}

#endif

void INIT_LIST_HEAD(struct list_head *list)
{
	WRITE_ONCE_LIST(list->next, list);
	list->prev = list;
}

/*
* Insert a newl entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static void __list_add(struct list_head *newl,
	struct list_head *prev,
	struct list_head *next)
{
	if (!__list_add_valid(newl, prev, next))
		return;

	next->prev = newl;
	newl->next = next;
	newl->prev = prev;
	WRITE_ONCE_LIST(prev->next, newl);
}

/**
* list_add - add a newl entry
* @newl: newl entry to be added
* @head: list head to add it after
*
* Insert a newl entry after the specified head.
* This is good for implementing stacks.
*/
void list_add(struct list_head *newl, struct list_head *head)
{
	__list_add(newl, head, head->next);
}

/**
* list_add_tail - add a newl entry
* @newl: newl entry to be added
* @head: list head to add it before
*
* Insert a newl entry before the specified head.
* This is useful for implementing queues.
*/
void list_add_tail(struct list_head *newl, struct list_head *head)
{
	__list_add(newl, head->prev, head);
}

/*
* Delete a list entry by making the prev/next entries
* point to each other.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static void __list_del(struct list_head * prev, struct list_head * next)
{
	next->prev = prev;
	WRITE_ONCE_LIST(prev->next, next);
}

/*
* Delete a list entry and clear the 'prev' pointer.
*
* This is a special-purpose list clearing method used in the networking code
* for lists allocated as per-cpu, where we don't want to incur the extra
* WRITE_ONCE_LIST() overhead of a regular list_del_init(). The code that uses this
* needs to check the node 'prev' pointer instead of calling list_empty().
*/

#if 0
static void __list_del_clearprev(struct list_head *entry)
{
	__list_del(entry->prev, entry->next);
	entry->prev = NULL;
}
#endif

/**
* list_del - deletes entry from list.
* @entry: the element to delete from the list.
* Note: list_empty() on entry does not return true after this, the entry is
* in an undefined state.
*/
static void __list_del_entry(struct list_head *entry)
{
	if (!__list_del_entry_valid(entry))
		return;

	__list_del(entry->prev, entry->next);
}

void list_del(struct list_head *entry)
{
	__list_del_entry(entry);
	entry->next = LIST_POISON1;
	entry->prev = LIST_POISON2;
}

/**
* list_replace - replace old entry by newl one
* @old : the element to be replaced
* @newl : the newl element to insert
*
* If @old was empty, it will be overwritten.
*/
void list_replace(struct list_head *old,
	struct list_head *newl)
{
	newl->next = old->next;
	newl->next->prev = newl;
	newl->prev = old->prev;
	newl->prev->next = newl;
}

void list_replace_init(struct list_head *old,
	struct list_head *newl)
{
	list_replace(old, newl);
	INIT_LIST_HEAD(old);
}

/**
* list_swap - replace entry1 with entry2 and re-add entry1 at entry2's position
* @entry1: the location to place entry2
* @entry2: the location to place entry1
*/
void list_swap(struct list_head *entry1,
	struct list_head *entry2)
{
	struct list_head *pos = entry2->prev;

	list_del(entry2);
	list_replace(entry1, entry2);
	if (pos == entry1)
		pos = entry2;
	list_add(entry1, pos);
}

/**
* list_del_init - deletes entry from list and reinitialize it.
* @entry: the element to delete from the list.
*/
void list_del_init(struct list_head *entry)
{
	__list_del_entry(entry);
	INIT_LIST_HEAD(entry);
}

/**
* list_move - delete from one list and add as another's head
* @list: the entry to move
* @head: the head that will precede our entry
*/
void list_move(struct list_head *list, struct list_head *head)
{
	__list_del_entry(list);
	list_add(list, head);
}

/**
* list_move_tail - delete from one list and add as another's tail
* @list: the entry to move
* @head: the head that will follow our entry
*/
void list_move_tail(struct list_head *list,
	struct list_head *head)
{
	__list_del_entry(list);
	list_add_tail(list, head);
}

/**
* list_bulk_move_tail - move a subsection of a list to its tail
* @head: the head that will follow our entry
* @first: first entry to move
* @last: last entry to move, can be the same as first
*
* Move all entries between @first and including @last before @head.
* All three entries must belong to the same linked list.
*/
void list_bulk_move_tail(struct list_head *head,
	struct list_head *first,
	struct list_head *last)
{
	first->prev->next = last->next;
	last->next->prev = first->prev;

	head->prev->next = first;
	first->prev = head->prev;

	last->next = head;
	head->prev = last;
}

/**
* list_is_first -- tests whether @list is the first entry in list @head
* @list: the entry to test
* @head: the head of the list
*/
int list_is_first(const struct list_head *list,
	const struct list_head *head)
{
	return list->prev == head;
}

/**
* list_is_last - tests whether @list is the last entry in list @head
* @list: the entry to test
* @head: the head of the list
*/
int list_is_last(const struct list_head *list,
	const struct list_head *head)
{
	return list->next == head;
}

/**
* list_empty - tests whether a list is empty
* @head: the list to test.
*/
int list_empty(const struct list_head *head)
{
	return READ_ONCE_LIST(head->next) == head;
}

/**
* list_empty_careful - tests whether a list is empty and not being modified
* @head: the list to test
*
* Description:
* tests whether a list is empty _and_ checks that no other CPU might be
* in the process of modifying either member (next or prev)
*
* NOTE: using list_empty_careful() without synchronization
* can only be safe if the only activity that can happen
* to the list entry is list_del_init(). Eg. it cannot be used
* if another CPU could re-list_add() it.
*/
int list_empty_careful(const struct list_head *head)
{
	struct list_head *next = head->next;
	return (next == head) && (next == head->prev);
}

/**
* list_rotate_left - rotate the list to the left
* @head: the head of the list
*/
void list_rotate_left(struct list_head *head)
{
	struct list_head *first;

	if (!list_empty(head)) {
		first = head->next;
		list_move_tail(first, head);
	}
}

/**
* list_rotate_to_front() - Rotate list to specific item.
* @list: The desired newl front of the list.
* @head: The head of the list.
*
* Rotates list so that @list becomes the newl front of the list.
*/
void list_rotate_to_front(struct list_head *list,
	struct list_head *head)
{
	/*
	* Deletes the list head from the list denoted by @head and
	* places it as the tail of @list, this effectively rotates the
	* list so that @list is at the front.
	*/
	list_move_tail(head, list);
}

/**
* list_is_singular - tests whether a list has just one entry.
* @head: the list to test.
*/
int list_is_singular(const struct list_head *head)
{
	return !list_empty(head) && (head->next == head->prev);
}

static void __list_cut_position(struct list_head *list,
	struct list_head *head, struct list_head *entry)
{
	struct list_head *new_first = entry->next;
	list->next = head->next;
	list->next->prev = list;
	list->prev = entry;
	entry->next = list;
	head->next = new_first;
	new_first->prev = head;
}

/**
* list_cut_position - cut a list into two
* @list: a newl list to add all removed entries
* @head: a list with entries
* @entry: an entry within head, could be the head itself
*	and if so we won't cut the list
*
* This helper moves the initial part of @head, up to and
* including @entry, from @head to @list. You should
* pass on @entry an element you know is on @head. @list
* should be an empty list or a list you do not care about
* losing its data.
*
*/
void list_cut_position(struct list_head *list,
	struct list_head *head, struct list_head *entry)
{
	if (list_empty(head))
		return;
	if (list_is_singular(head) &&
		(head->next != entry && head != entry))
		return;
	if (entry == head)
		INIT_LIST_HEAD(list);
	else
		__list_cut_position(list, head, entry);
}

/**
* list_cut_before - cut a list into two, before given entry
* @list: a newl list to add all removed entries
* @head: a list with entries
* @entry: an entry within head, could be the head itself
*
* This helper moves the initial part of @head, up to but
* excluding @entry, from @head to @list.  You should pass
* in @entry an element you know is on @head.  @list should
* be an empty list or a list you do not care about losing
* its data.
* If @entry == @head, all entries on @head are moved to
* @list.
*/
void list_cut_before(struct list_head *list,
	struct list_head *head,
	struct list_head *entry)
{
	if (head->next == entry) {
		INIT_LIST_HEAD(list);
		return;
	}
	list->next = head->next;
	list->next->prev = list;
	list->prev = entry->prev;
	list->prev->next = list;
	head->next = entry;
	entry->prev = head;
}

static void __list_splice(const struct list_head *list,
	struct list_head *prev,
	struct list_head *next)
{
	struct list_head *first = list->next;
	struct list_head *last = list->prev;

	first->prev = prev;
	prev->next = first;

	last->next = next;
	next->prev = last;
}

/**
* list_splice - join two lists, this is designed for stacks
* @list: the newl list to add.
* @head: the place to add it in the first list.
*/
void list_splice(const struct list_head *list,
	struct list_head *head)
{
	if (!list_empty(list))
		__list_splice(list, head, head->next);
}

/**
* list_splice_tail - join two lists, each list being a queue
* @list: the newl list to add.
* @head: the place to add it in the first list.
*/
void list_splice_tail(struct list_head *list,
	struct list_head *head)
{
	if (!list_empty(list))
		__list_splice(list, head->prev, head);
}

/**
* list_splice_init - join two lists and reinitialise the emptied list.
* @list: the newl list to add.
* @head: the place to add it in the first list.
*
* The list at @list is reinitialised
*/
void list_splice_init(struct list_head *list,
	struct list_head *head)
{
	if (!list_empty(list)) {
		__list_splice(list, head, head->next);
		INIT_LIST_HEAD(list);
	}
}

/**
* list_splice_tail_init - join two lists and reinitialise the emptied list
* @list: the newl list to add.
* @head: the place to add it in the first list.
*
* Each of the lists is a queue.
* The list at @list is reinitialised
*/
void list_splice_tail_init(struct list_head *list,
	struct list_head *head)
{
	if (!list_empty(list)) {
		__list_splice(list, head->prev, head);
		INIT_LIST_HEAD(list);
	}
}
